package cloud.xlh.my_system.dynamic_env_refresh;

import cloud.xlh.infrastructure.utils.FileUtil;
import cloud.xlh.infrastructure.utils.StringUtil;
import cloud.xlh.my_system.event.ServiceCallChangeEvent;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.client.utils.LogUtils;
import com.alibaba.nacos.common.lifecycle.Closeable;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.http.*;
import org.springframework.util.CollectionUtils;
import org.springframework.web.client.RestTemplate;

import java.util.*;
import java.util.concurrent.*;
import java.util.function.Consumer;

/**
 * Created with IntelliJ IDEA.
 *
 * @ project name : xlh_osms
 * @ Author: XuLeHuang
 * @ Date: 2023/3/10 10:13
 * @ Description:
 */
//@Component
@Slf4j
public class ClientWorker implements Closeable {
    private static final Logger LOGGER = LogUtils.logger(ClientWorker.class);
    final ScheduledExecutorService executor;
    final java.util.concurrent.ScheduledExecutorService executorService;
    private long timeout;
    //todo 后期定义一个类似nacos的CacheDate的数据结构
    private final ConcurrentHashMap<String, CacheData> cacheMap = new ConcurrentHashMap();
    public String probeUpdateString;


    /**
     * @return
     * @author XuLeHuang
     * @create 2023/3/12
     * @name:
     * @Description: ClientWorker在初始化的时候就会初始化两个定时调度线程池，
     * 以及启动一个定时任务，该定时任务会执行ClientWorker.checkConfigInfo() 方法（10ms执行一次）
     **/

    public ClientWorker() {
       /* this.configFilterChainManager = configFilterChainManager;
        this.init(properties);*/
        //this.timeout = (long)Math.max(ConvertUtils.toInt(properties.getProperty("configLongPollTimeout"), 30000), 10000);
        this.init();
        //初始化一个定时调度的线程池
        this.executor = Executors.newScheduledThreadPool(1, r -> {
            Thread t = new Thread(r);
            t.setName("cloud.xlh.my_system.dynamic_env_refresh.client.worker");
            t.setDaemon(true);
            return t;
        });
        //初始化一个长轮询的定时调度线程池
        this.executorService = Executors.newScheduledThreadPool(Runtime.getRuntime().availableProcessors(), new ThreadFactory() {
            public Thread newThread(Runnable r) {
                Thread t = new Thread(r);
                t.setName("cloud.xlh.my_system.dynamic_env_refresh.client.worker.longPolling");
                t.setDaemon(true);
                return t;
            }
        });
        //首次执行延迟1ms，之后10ms
        this.executor.scheduleWithFixedDelay(() -> {
            try {
                checkConfigInfo();
            } catch (Throwable var2) {
                //com.alibaba.nacos.client.config.impl.ClientWorker.LOGGER.error("[" + agent.getName() + "] [sub-check] rotate check error", var2);
                LOGGER.error("this.executor.scheduleWithFixedDelay get error");
            }

        }, 1L, 10L, TimeUnit.MILLISECONDS);
    }

    private void init() {
        this.timeout = 30000;
        /*JSONObject config2JsonObject = FileUtil.FileStrToJSONObject(FileUtil.PathTypeCode.CLASS_PATH.getCode(), "serviceCallList.json");
        String config2JsonString = config2JsonObject.toJSONString();
        String svcCodeForValueStr = JSONObjectUtil.getAnyNodeDataFromJsonStr(config2JsonString, "svcCode");
*/
        Properties remoteProperties = FileUtil.readApplicationYMLFileAndTurnToProperties("D:\\development\\environment\\idea\\IdeaProjects\\xlh_osms\\module\\my-system\\src\\main\\resources\\application.yml", FileUtil.PathTypeCode.ABSOLUTE_PATH.getCode());
        String remoteServiceCallListFileLocation = remoteProperties.getProperty("remote.serviceCallListFile.location");
        Map map = FileUtil.readFromJsonFile(remoteServiceCallListFileLocation, null);
        String svcCodeForValueStr = (String) map.get("svcCode");

        CacheData cacheData = new CacheData();
        cacheData.setKey("svcCode");
        cacheData.setValue(svcCodeForValueStr);
        this.cacheMap.put("serviceCallList" , cacheData);
    }

    /**
     * @return void
     * @author XuLeHuang
     * @create 2023/3/12
     * @name:
     * @Description: 该任务就是用来分配任务的，设定每3000个配置文件为一个分片，
     * 每个分片都会开启一个异步任务放到线程池中处理该分片的配置文件，异步任务为LongPollingRunnable
     *               todo 这里未来配置多就开启多线程处理
     **/
    public void checkConfigInfo() {
        this.executorService.execute(new ClientWorker.LongPollingRunnable());
    }

    @Override
    public void shutdown() throws NacosException {

    }

    class LongPollingRunnable implements Runnable {
        @Override
        public void run() {
            try {
                //todo 将cacheMap转成List<Object>
                Set<Map.Entry<String, CacheData>> entrySet = cacheMap.entrySet();
                List<CacheData> cacheDataList = new ArrayList<>();
                entrySet.forEach(new Consumer<Map.Entry<String, CacheData>>() {
                    @Override
                    public void accept(Map.Entry<String, CacheData> stringCacheDataEntry) {
                        CacheData cacheData = stringCacheDataEntry.getValue();
                        cacheDataList.add(cacheData);
                    }
                });

                ArrayList inInitializingCacheList = new ArrayList();
                log.info("cacheDataList->{}" , cacheDataList);
                List<String> changedGroupKeys = ClientWorker.this.checkUpdateDataIds(cacheDataList, inInitializingCacheList);
                //todo 通知修改环境配置
                //todo 更新cacheMap和发布事件
                if (changedGroupKeys != null && changedGroupKeys.size() > 0) {
                    String changeConfig = changedGroupKeys.get(0);
                    applicationContext.publishEvent(new ServiceCallChangeEvent(this , changeConfig , cacheMap));
                }
               } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    List<String> checkUpdateDataIds(List<CacheData> cacheDatas, List<String> inInitializingCacheList) throws Exception {
        //todo 将cacheDatas装成String；可拓展，多cacheData
        CacheData cacheData = cacheDatas.get(0);
        StringBuilder sb = new StringBuilder();
        sb.append(cacheData.getKey());
        sb.append(":");
        sb.append(cacheData.getValue());
        return this.checkUpdateConfigStr(sb.toString(), false);
    }

    @Autowired
    private ApplicationContext applicationContext;

    List<String> checkUpdateConfigStr(String probeUpdateString, boolean isInitializingCacheList) throws Exception {
        Map<String, String> params = new HashMap(2);
        if (StringUtil.isEmpty(probeUpdateString)) {
            probeUpdateString = "监听的配置的字符串形式";
        }
        log.info("probeUpdateString->{}" , probeUpdateString);
        params.put("Listening-Configs", probeUpdateString);
        //长轮询结束的时间
        Map<String, String> headers = new HashMap(2);
        headers.put("Long-Pulling-Timeout", "" + this.timeout);
        long readTimeoutMs = this.timeout + (long) Math.round((float) (this.timeout >> 1));
        //HttpRestResult<String> result = this.agent.httpPost("/v1/cs/configs/listener", headers, params, this.agent.getEncode(), readTimeoutMs);
        //todo 发送http请求
        HttpHeaders httpHeaders = new HttpHeaders();
        if (!CollectionUtils.isEmpty(headers)) {
            for (Map.Entry<String, String> entry : headers.entrySet()) {
                httpHeaders.set(entry.getKey(), entry.getValue());
            }
        }
        if (!CollectionUtils.isEmpty(params)) {
            for (Map.Entry<String, String> entry : params.entrySet()) {
                httpHeaders.set(entry.getKey(), entry.getValue());
            }
        }
        httpHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        HttpEntity<Object> request = new HttpEntity<>(httpHeaders);
        RestTemplate restTemplate = new RestTemplate();
        ResponseEntity<String> responseEntity = restTemplate.exchange("http://localhost:9090/v1/cs/configs/listener", HttpMethod.POST, request, String.class);
        //restTemplateUtil.postForObject("http://localhost:9090/configs/listener" , null , null , params , String.class ,null );
        System.out.println(responseEntity);
        if (HttpStatus.OK.value() == responseEntity.getStatusCodeValue()) {
            return this.parseUpdateDataIdResponse((String)responseEntity.getBody());
        }
        return Collections.emptyList();
    }

    private List<String> parseUpdateDataIdResponse(String body) {
        if (StringUtil.isEmpty(body)) {
            return Collections.emptyList();
        }
        List<String> list = new ArrayList<>();
        list.add(body);
        return list;
    }
}
